#!/usr/bin/env python
#
# Copyright 2009 Google Inc.
"""
For a given URL, generate a CSE specification and an annotations file.

The specification is created by adding a URL to django template,
specifications.xml. The corresponding annotations file is included in the
specification and generated with a django template, annotations.xml.

The annotations are generated using Google Custom Search Engine's
makeannotations service: 'http://google.com/cse/tools/makeannotations?url='
Each annotation is given two of three possible labels:
    _cse_wikipedia is background label applied to all
    internal is label for current domain links
    external is label for external links
"""

__author__ = 'skidgel@google.com (John Skidgel)'

# Standard Python imports.
import os
import re
import sys
import urllib
import urllib2
import wsgiref.handlers
from urlparse import urlparse

# App Engine imports.
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template


# Set to true to have the webapp print stack traces.
_DEBUG = False

# CSE Make annotations constant.
MAKE_ANNOTATIONS_URI = 'http://google.com/cse/tools/makeannotations?'

# Handlers
# -----------------------------------------------------------------------------

class BaseRequestHandler(webapp.RequestHandler):
  """Base class for all requests."""

  def generate(self, template_name, template_values={}):
    """Return a page given a request.
    Args:
      template_name: The name of the django template to render parameters into
          and return.
      template_values: Template variables to append to values.
    """
    values = {
      'request': self.request,
      'debug': self.request.get('deb')
    }
    values.update(template_values)
    directory = os.path.dirname(__file__)
    path = os.path.join(directory, os.path.join('templates', template_name))
    self.response.out.write(template.render(path, values, debug=_DEBUG))


class Home(BaseRequestHandler):
  """A home page that includes a form for testing creation of the
  CSE Specification and Annotations XML files."""

  def get(self):
    self.generate('index.html', {})


class DomainSpecification(BaseRequestHandler):
  """Request handler that creates the CSE specification."""

  def get(self):
    self.response.headers['Content-Type'] = 'text/xml'
    if self.request.get('url') is not None:
      url = urllib.quote(self.request.get('url'))
      url = urllib.unquote(url)
      location = urlparse(url)
      host_name = location.hostname
      host_name_parts = host_name.split('.')
      domain = host_name_parts[0]

      self.generate('domain_specification.xml', { 'domain': domain })
    else:
      self.response.out.write('<error>no url</error>')


class Specification(BaseRequestHandler):
  """Request handler that creates the CSE specification."""

  def get(self):
    self.response.headers['Content-Type'] = 'text/xml'
    if self.request.get('url') is not None:
      # TODO(skidgel): Verify that the string is a valid URL pattern.
      url = urllib.quote(self.request.get('url'))
      self.generate('specification.xml', { 'url': url })
    else:
      self.response.out.write('<error>no url</error>')


class Annotations(BaseRequestHandler):
  """Request handler that creates annotations for a given URL."""

  def get(self):
    self.response.headers['Content-Type'] = 'text/xml'
    if self.request.get('url') is not None:

      # Get the url from the request and retrieve its annotations.
      url = self.request.get('url')
      request_query = {'url': url, 'label': 'arbitrary', 'pattern': 'exact'}
      request_url = MAKE_ANNOTATIONS_URI + urllib.urlencode(request_query)
      try:
        annotations_xml = urllib2.urlopen(request_url)
      except URLError:
        self.response.out.write(
            '<error>The annotations could not be retrieved.</error>')

      # Create a dictionary of annotations for the template.
      annotations = []
      annotation_pattern = re.compile('  <Annotation about="([^"]*)">')
      domain_pattern = re.compile('wikipedia\.org')
      for line in annotations_xml:
        match = annotation_pattern.match(line)
        if match:
          annotation = {}
          annotation['url'] = match.group(1)
          if domain_pattern.search(match.group(1)):
            annotation['location_label'] = 'internal'
          else:
            annotation['location_label'] = 'external'
          annotations.append(annotation)

      # Return the annotations xml.
      self.generate('annotations.xml', { 'annotations': annotations })

    else:
      self.response.out.write('<error>no url</error>')


def main():
  # Create a web application using web app and map the following
  # url patterns to specific classes.
  application = webapp.WSGIApplication([('/', Home),
                                        ('/wikipedia/domain_spec.do', DomainSpecification),
                                        ('/wikipedia/spec.do', Specification),
                                        ('/wikipedia/annotations.do', Annotations)
                                       ], debug = True)
  wsgiref.handlers.CGIHandler().run(application)


if __name__ == '__main__':
  main()
